/**
 * Token缓存模块
 * 
 * 提供统一的Token获取、缓存和管理功能
 * - 缓存Token，有效期1小时
 * - 自动刷新过期Token
 */

const fs = require('fs');
const path = require('path');
const http = require('http');
const { apiLogger } = require('./apiLogger');

// 常量定义
const TOKEN_CACHE_EXPIRE = 60 * 60 * 1000; // 1小时过期
const TOKEN_CACHE_DIR = path.resolve(__dirname, '../.cache');
const TOKEN_CACHE_FILE = path.join(TOKEN_CACHE_DIR, 'token-cache.json');

// 确保缓存目录存在
if (!fs.existsSync(TOKEN_CACHE_DIR)) {
    fs.mkdirSync(TOKEN_CACHE_DIR, { recursive: true });
}

/**
 * Token缓存工具
 */
class TokenCache {
    constructor(options = {}) {
        this.options = {
            apiBaseUrl: '',
            projectId: '',
            schema: '',
            tokenCacheExpire: TOKEN_CACHE_EXPIRE,
            ...options
        };

        // 初始化API日志记录器
        this.apiLogger = apiLogger;
    }

    /**
     * 保存Token到缓存
     * @param {string} token Token字符串
     * @returns {object|null} 缓存数据对象或null
     */
    saveToken(token) {
        try {
            const cacheData = {
                token: token,
                projectId: this.options.projectId,
                schema: this.options.schema,
                timestamp: Date.now(),
                expireTime: Date.now() + this.options.tokenCacheExpire,
                source: 'api'
            };

            fs.writeFileSync(TOKEN_CACHE_FILE, JSON.stringify(cacheData, null, 2));
            console.log(`Token已缓存，有效期至: ${new Date(cacheData.expireTime).toISOString()} (${Math.floor(this.options.tokenCacheExpire / 1000 / 60)}分钟)`);
            return cacheData;
        } catch (error) {
            console.error('保存Token缓存失败', error);
            return null;
        }
    }

    /**
     * 从缓存获取Token
     * @returns {string|null} Token字符串或null
     */
    getToken() {
        try {
            if (!fs.existsSync(TOKEN_CACHE_FILE)) {
                console.log('Token缓存文件不存在');
                return null;
            }

            const cacheData = JSON.parse(fs.readFileSync(TOKEN_CACHE_FILE, 'utf8'));
            const now = Date.now();

            // 检查token是否已过期
            if (now > cacheData.expireTime) {
                console.log(`Token已过期，过期时间: ${new Date(cacheData.expireTime).toISOString()}`);
                return null;
            }

            // 检查projectId和schema是否匹配
            if (cacheData.projectId !== this.options.projectId || cacheData.schema !== this.options.schema) {
                console.log('Token缓存的项目ID或Schema与当前不匹配，不使用缓存');
                console.log(`缓存: projectId=${cacheData.projectId}, schema=${cacheData.schema}`);
                console.log(`当前: projectId=${this.options.projectId}, schema=${this.options.schema}`);
                return null;
            }

            const remainTime = Math.floor((cacheData.expireTime - now) / 1000 / 60);
            console.log(`使用缓存Token，来源: ${cacheData.source || 'unknown'}，剩余有效时间: ${remainTime} 分钟`);

            return cacheData.token;
        } catch (error) {
            console.error('读取Token缓存失败', error);
            return null;
        }
    }

    /**
     * 清除Token缓存
     * @returns {boolean} 操作是否成功
     */
    clearToken() {
        try {
            if (fs.existsSync(TOKEN_CACHE_FILE)) {
                fs.unlinkSync(TOKEN_CACHE_FILE);
                console.log('Token缓存已清除');
                return true;
            }
            return false;
        } catch (error) {
            console.error('清除Token缓存失败', error);
            return false;
        }
    }

    /**
     * 发起API请求
     * @param {string} url 请求URL
     * @param {string} method 请求方法 (GET, POST等)
     * @param {object} body 请求体
     * @param {object} headers 请求头
     * @param {boolean} useToken 是否使用Token认证
     * @returns {Promise<object>} 响应对象
     */
    async fetchApi(url, method, body, headers = {}, useToken = true) {
        console.log(`发起API请求: ${method} ${url}`);

        // 添加Token到请求头（如果需要）
        if (useToken) {
            const token = await this.ensureToken();
            if (!token) {
                throw new Error('无法获取有效的认证Token，API请求终止');
            }

            // 构建完整的浏览器请求头
            const browserHeaders = {
                'Accept': 'application/json, text/plain, */*',
                'Accept-Language': 'zh-CN,zh;q=0.9',
                'Accopt-Encoding': 'utf8',
                'Content-Type': 'application/json;charset=UTF-8',
                'Origin': this.options.apiBaseUrl,
                'Proxy-Connection': 'keep-alive',
                'Referer': `${this.options.apiBaseUrl}/index.html`,
                'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36',
                'x-token': token,
                ...headers // 合并用户提供的请求头
            };

            // 更新headers为合并后的完整请求头
            headers = browserHeaders;
        }

        // 记录API请求
        const requestId = this.apiLogger.logRequest(method, url, headers, body);

        return new Promise((resolve, reject) => {
            // 解析URL
            const urlObj = new URL(url);

            // 请求选项
            const options = {
                hostname: urlObj.hostname,
                port: urlObj.port || 80,
                path: urlObj.pathname + urlObj.search,
                method: method,
                headers: {
                    'Content-Type': 'application/json',
                    ...headers
                }
            };

            // 如果有请求体，添加Content-Length
            if (body) {
                const bodyString = JSON.stringify(body);
                options.headers['Content-Length'] = Buffer.byteLength(bodyString);
            }

            // 创建请求
            const req = http.request(options, (res) => {
                let data = '';

                // 接收数据
                res.on('data', (chunk) => {
                    data += chunk;
                });

                // 数据接收完成
                res.on('end', () => {
                    try {
                        const parsedData = JSON.parse(data);
                        console.log(`API请求成功: ${method} ${url} (状态码: ${res.statusCode})`);

                        // 记录API响应
                        this.apiLogger.logResponse(requestId, res.statusCode, res.headers, parsedData);

                        // 特殊处理表列表响应 - 检查是否包含tablelist字段
                        if (parsedData && parsedData.tablelist && Array.isArray(parsedData.tablelist)) {
                            console.log(`检测到表列表响应格式，提取tablelist数组 (${parsedData.tablelist.length}个表)`);
                            resolve(parsedData.tablelist);
                        } else {
                            resolve(parsedData);
                        }
                    } catch (e) {
                        const errorMsg = `解析响应失败: ${e.message}`;
                        console.error(errorMsg);

                        // 记录API响应错误
                        this.apiLogger.logResponse(requestId, res.statusCode, res.headers, data, e);

                        reject(new Error(errorMsg));
                    }
                });
            });

            // 处理错误
            req.on('error', (e) => {
                const errorMsg = `请求失败: ${e.message}`;
                console.error(errorMsg);

                // 记录API请求错误
                this.apiLogger.logResponse(requestId, 0, null, null, e);

                reject(new Error(errorMsg));
            });

            // 发送请求体
            if (body) {
                req.write(JSON.stringify(body));
            }

            req.end();
        });
    }

    /**
     * 确保有有效Token可用，必要时获取新Token
     * @param {boolean} forceRefresh 是否强制刷新
     * @param {number} retryCount 重试计数
     * @returns {Promise<string|null>} Token字符串或null
     */
    async ensureToken(forceRefresh = false, retryCount = 0) {
        try {
            // 如果强制刷新，清除缓存
            if (forceRefresh) {
                console.log('强制刷新Token，清除缓存');
                this.clearToken();
            }

            // 尝试从缓存获取token
            const cachedToken = this.getToken();
            if (cachedToken && !forceRefresh) {
                console.log('从缓存获取token成功');
                return cachedToken;
            }

            // 缓存中没有有效token，从API获取
            console.log('尝试从API获取token...');

            // 构建请求头，模拟浏览器请求
            const requestHeaders = {
                'Accept': 'application/json, text/plain, */*',
                'Accept-Language': 'zh-CN,zh;q=0.9',
                'Accopt-Encoding': 'utf8',
                'Content-Type': 'application/json;charset=UTF-8',
                'Origin': this.options.apiBaseUrl,
                'Referer': `${this.options.apiBaseUrl}/index.html`,
                'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36'
            };

            // 根据CURL示例修改请求URL和请求体
            const response = await this.fetchApi(
                `${this.options.apiBaseUrl}/get_token?tbName=prjGet_token`,
                'POST',
                { id: this.options.projectId }, // 使用id字段而不是project_id
                requestHeaders,
                false // 不使用Token认证
            );

            // 解析响应，获取token
            // 注意：根据具体响应结构可能需要调整这里的解析方式
            const token = response.data?.token || response.token;

            if (!token) {
                throw new Error('API响应中未找到token字段');
            }

            console.log('从API获取token成功');

            // 缓存token
            this.saveToken(token);

            return token;
        } catch (error) {
            console.error(`获取token失败 (尝试 ${retryCount + 1}/3)`, error);

            // 如果未达到最大重试次数，尝试重新获取
            if (retryCount < 2) {
                console.log(`等待1秒后重试获取token (${retryCount + 2}/3)...`);
                await new Promise(resolve => setTimeout(resolve, 1000)); // 等待1秒
                return this.ensureToken(forceRefresh, retryCount + 1);
            }

            throw error;
        }
    }

    /**
     * 获取并准备所有API调用所需的Token和请求头
     * @returns {Promise<object>} 请求头对象
     */
    async getRequestHeaders() {
        const token = await this.ensureToken();
        if (!token) {
            throw new Error('无法获取有效的认证Token');
        }

        return {
            'x-token': token,
            'Content-Type': 'application/json;charset=UTF-8',
            'Accept': 'application/json, text/plain, */*',
            'Accept-Language': 'zh-CN,zh;q=0.9',
            'Accopt-Encoding': 'utf8',
            'Origin': this.options.apiBaseUrl,
            'Proxy-Connection': 'keep-alive',
            'Referer': `${this.options.apiBaseUrl}/index.html`,
            'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36'
        };
    }
}

module.exports = TokenCache; 